#include "rsucommforshineg60e.h"
#include <QTimer>
#include <QTime>
#include <QtEndian>

RsuCommForShineG60E::RsuCommForShineG60E(QObject *parent) : QObject(parent)
{
    m_bReadInBlockMode=false;

    connect(&m_tcpSocket, &QTcpSocket::connected,
            this, &RsuCommForShineG60E::socketConnected);
    connect(&m_tcpSocket, &QTcpSocket::disconnected,
            this, &RsuCommForShineG60E::socketDisconnected);
    // connect(&m_tcpSocket, &QTcpSocket::bytesWritten,
    //         this, &RsuComm::socketBytesWritten);
    connect(&m_tcpSocket, &QTcpSocket::readyRead,
            this, &RsuCommForShineG60E::socketReadyRead);
    connect(&m_tcpSocket, &QAbstractSocket::stateChanged,
             this, &RsuCommForShineG60E::stateChanged);
    connect(&m_tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)),
            this, SLOT(socketError(QAbstractSocket::SocketError)));
}

RsuCommForShineG60E::~RsuCommForShineG60E()
{
}

void RsuCommForShineG60E::openRsu(const QString &host, quint16 port)
{
    m_sHostAddr = host;
    m_nHostPort = port;
    tryReconnect();
}

void RsuCommForShineG60E::closeRsu()
{
    m_tcpSocket.close();
}

// 帧头
const static char *STX="\xFF\xFF";
// 帧尾
const static char *ETX="\xFF";
// 除去帧头、帧尾的最小帧长度：RSCTL(1) TYPE(1) BCC(1)
const static int MIN_FRAME_SIZE=3;
// 带帧头、帧尾的最小帧长度
const static int MIN_FRAME_SIZE_WITH_FRAME_HEAD_AND_END=6;

void RsuCommForShineG60E::socketConnected()
{
    logger()->debug(tr("<%1:%2>: connected.")
                    .arg(m_sHostAddr)
                    .arg(m_nHostPort));
}

void RsuCommForShineG60E::socketDisconnected()
{
    logger()->debug(tr("<%1:%2>: disconnected.")
                    .arg(m_sHostAddr)
                    .arg(m_nHostPort));
}

void RsuCommForShineG60E::socketBytesWritten(qint64 bytes)
{
    logger()->debug(tr("<%1:%2>: [%3] bytes written.")
                    .arg(m_sHostAddr)
                    .arg(m_nHostPort)
                    .arg(bytes));
}

void RsuCommForShineG60E::logBytesRead(const QByteArray &dataRead, const QString &flag)
{
    logger()->debug(tr("<%1:%2>: <<[%3] [%4] bytes - [%5]")
                    .arg(m_sHostAddr)
                    .arg(m_nHostPort)
                    .arg(flag)
                    .arg(dataRead.length())
                    .arg(dataRead.toHex().data()));
}

void RsuCommForShineG60E::socketReadyRead()
{
    if (m_bReadInBlockMode)
    {
        return;
    }

    QByteArray dataRead=m_tcpSocket.readAll();
    logBytesRead(dataRead, "a");
    if (dataRead.length()==0)
    {
        logger()->info(tr("<%1:%2>: 0 byte read, disconnect.")
                       .arg(m_sHostAddr)
                       .arg(m_nHostPort));
        m_tcpSocket.disconnectFromHost();
        return;
    }
    processDataRead(dataRead, NULL, 0);
}

bool RsuCommForShineG60E::processDataRead(const QByteArray &dataRead, QByteArray *pExpectedFrame, quint8 nExpectedFrameType)
{
    m_dataRead.append(dataRead);

    bool bGotExpectedFrame=false;
    while (m_dataRead.length()>MIN_FRAME_SIZE_WITH_FRAME_HEAD_AND_END)
    {
        int start = m_dataRead.indexOf(STX, 0);
        if (start<0)
        {
            // 没有包头，丢弃所有数据
            logger()->debug(tr("<%1:%2>: no frame head, discard %3 bytes.")
                            .arg(m_sHostAddr)
                            .arg(m_nHostPort)
                            .arg(m_dataRead.length()));
            m_dataRead.clear();
            return false;
        }
        int end = m_dataRead.indexOf(ETX, strlen(STX));
        if (end<0)
        {
            // 没有包尾，等待再次接收数据
            return false;
        }
        // 抽取去除包头、包尾的帧数据
        QByteArray frame = m_dataRead.mid(start+strlen(STX), end-start-strlen(STX));
        // 从读入缓存中去除本帧数据（连同包头、包尾）
        m_dataRead.remove(0, end+strlen(ETX));
        if (processRawFrame(frame, pExpectedFrame, nExpectedFrameType))
        {
            bGotExpectedFrame=true;
        }
    }
    return bGotExpectedFrame;
}

void RsuCommForShineG60E::mockRawFrameData(QByteArray &frame)
{
    processRawFrame(frame, NULL, 0);
}

bool RsuCommForShineG60E::processRawFrame(QByteArray &frame, QByteArray *pExpectedFrame, quint8 nExpectedFrameType)
{
    logger()->debug(tr("<%1:%2>: frame[%3]")
                    .arg(m_sHostAddr)
                    .arg(m_nHostPort)
                    .arg(frame.toHex().data()));
    // 长度检查
    if (frame.length()<MIN_FRAME_SIZE)
    {
        // 长度不足，丢弃帧数据
        logger()->debug(tr("<%1:%2>: too short, discard frame[%3]")
                        .arg(m_sHostAddr)
                        .arg(m_nHostPort)
                        .arg(frame.toHex().data()));
        return false;
    }

    // BCC校验
    quint8 bcc = getXOR(frame);
    if (0!=bcc)
    {
        // BCC校验失败，丢弃帧数据
        logger()->debug(tr("<%1:%2>: bcc error, discard frame[%3]")
                        .arg(m_sHostAddr)
                        .arg(m_nHostPort)
                        .arg(frame.toHex().data()));
        return false;
    }
    frame.replace("\xFE\x01", 2, "\xFF", 1);
    frame.replace("\xFE\x00", 2, "\xFE", 1);

    // logger()->debug(tr("<%1:%2>: frame2[%3]")
    //                 .arg(m_sHostAddr)
    //                 .arg(m_nHostPort)
    //                 .arg(frame.toHex().data()));

    quint8 nFrameIndex=frame[0];
    quint8 nFrameType=frame[1];
    // 去除帧序号和帧类型
    frame.remove(0, 2);
    // 去除BCC
    frame.remove(frame.length()-1, 1);
    if (NULL!=pExpectedFrame)
    {
        bool bRlt=(nFrameType==nExpectedFrameType);
        if (bRlt)
        {
            pExpectedFrame->clear();
            pExpectedFrame->append(frame);
        }
        return bRlt;
    }
    else
    {
        processFrame(nFrameIndex, nFrameType, frame);
        return false;
    }
}

quint8 RsuCommForShineG60E::getXOR(const QByteArray &data)
{
    quint8 n=0x00;
    for(int it=0; it<data.length(); ++it)
    {
        n ^= data[it];
    }
    return n;
}

void RsuCommForShineG60E::processFrame(quint8 nFrameIndex, quint8 nFrameType, QByteArray &frame)
{
    emit newFrameArrived(nFrameIndex, nFrameType, frame);
}

void RsuCommForShineG60E::socketStateChanged(QAbstractSocket::SocketState socketState)
{
    logger()->debug(tr("<%1:%2>: Socket状态变化[%3]")
                    .arg(m_sHostAddr)
                    .arg(m_nHostPort)
                    .arg(socketState));
}

void RsuCommForShineG60E::socketError(QAbstractSocket::SocketError socketError)
{
    logger()->debug(tr("<%1:%2>: Socket错误[%3][%4]")
                    .arg(m_sHostAddr)
                    .arg(m_nHostPort)
                    .arg(socketError)
                    .arg(this->m_tcpSocket.errorString()));
    QTimer::singleShot(10000, this, &RsuCommForShineG60E::tryReconnect);

}

void RsuCommForShineG60E::tryReconnect()
{
    if (QAbstractSocket::UnconnectedState==m_tcpSocket.state())
    {
        m_tcpSocket.connectToHost(m_sHostAddr, m_nHostPort);
    }
}

void RsuCommForShineG60E::wrapFrame(QByteArray &data)
{
    data.replace("\xFE", 1, "\xFE\x00", 2);
    data.replace("\xFF", 1, "\xFE\x01", 2);
    quint8 rsctl = getRSCTL();
    data.prepend(rsctl);
    quint8 bcc = getXOR(data);
    data.append(bcc);
    data.prepend(STX);
    data.append(ETX);
}

int RsuCommForShineG60E::wrapAndSendFrame(QByteArray &data)
{
    wrapFrame(data);
    logger()->debug(tr("<%1:%2>: >> %3")
                    .arg(m_sHostAddr)
                    .arg(m_nHostPort)
                    .arg(data.toHex().data()));

    // TODO: 判断返回值
    m_tcpSocket.write(data);
    return ERR_SUCCESS;
}

#define PRE_CHECK_FOR_RQ \
    if (QAbstractSocket::ConnectedState!=this->m_tcpSocket.state()) \
    { \
        return ERR_RSU_NOT_OPENED; \
    }

int RsuCommForShineG60E::initRsuRq(char *Time, int BSTInterval, int TxPower, int PLLChannelID)
{
    PRE_CHECK_FOR_RQ;

    /* 发送初始化RSU命令
    FF 80 C0 1D F9 61 92 20 15 12 08 09 38 26 03 02 0F 00 02 63 FF
       00 01 02          06                   13 14 15 16 17 18
    00	1	RSCTL	串口帧序列号
    01	1	CMDType	指令代码，此处取值C0H，H代表十六进制；
    02	4	Seconds	1970/1/1 0:0:0距离当前的秒数，高位在前
    06	7	Datetime	当前日期时间，yyyymmddhhmmss（7字节BCD码）
    13	1	LaneMode	车道模式，取值分别为3/4/6/7/8 分别为ETC入口、出口、开放式、标识站、合建站
    14	1	WaitTime	最小重读时间(单位：秒)
    15	1	TxPower	功率级数
    16	1	PLLChannelID	信道号（缺省为0）
    17	1	SendFlagInfo	是否处理标识站信息 0-处理，1-不处理
    18	1	BCC	异或校验值
    */

    QByteArray data;
    data.append(0xC0);

    Q_UNUSED(Time); // data.append(Time, 4);
    QDateTime current=QDateTime::currentDateTime();
    quint32 uTime = current.toTime_t();
    uTime=qToBigEndian(uTime);
    char buf[16];
    memcpy(buf, &uTime, 4);
    data.append(buf, 4);
    QByteArray sTime;
    sTime.append(current.toString("yyyyMMddhhmmss"));
    data.append(QByteArray::fromHex(sTime));
    data.append(0x03);
    Q_UNUSED(BSTInterval); // data.append(quint8(BSTInterval));
    data.append(0x02);
    data.append(quint8(TxPower));
    data.append(quint8(PLLChannelID));
    data.append(0x02);
    return wrapAndSendFrame(data);
}

int RsuCommForShineG60E::sendCmd(quint8 nCmd, quint32 nMacID)
{
    PRE_CHECK_FOR_RQ;

    /* 继续交易指令－C1 / 停止交易指令－C2

    FF 80 C1 02 CA 21 2F 87 FF
       00 01 02          06
    00	1	RSCTL	串口帧序列号
    01	1	CMDType	指令代码，此处取值C1H/C2H
    02	4	OBUID	OBU号(高字节在前)
    06	1	BCC	异或校验值
    */

    QByteArray data;
    data.append(nCmd);

    quint32 uTmp = qToBigEndian(nMacID);
    char buf[16];
    memcpy(buf, &uTmp, 4);
    data.append(buf, 4);
    return wrapAndSendFrame(data);
}

